﻿using DefCore.Core;
using DefCore.Interfaces;
using DefCore.Mapping;
using Ivory;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DefCore.Structures
{
    public class StructualFrame
    {
        //Fields
        public CellBase Parent { get; private set; }
        public StructureFrameEnum CurrentPlacement { get; private set; }

        public bool NorthWall { get { return IsOccuppied(StructureFrameEnum.NORTHFRAME); } }
        public bool EastWall { get { return IsOccuppied(StructureFrameEnum.EASTFRAME); } }
        public bool SouthWall { get { return IsOccuppied(StructureFrameEnum.SOUTHFRAME); } }
        public bool WestWall { get { return IsOccuppied(StructureFrameEnum.WESTFRAME); } }
        public bool Center { get { return IsOccuppied(StructureFrameEnum.CENTERFRAME); } }
        public bool FullFrame { get { return IsOccuppied(StructureFrameEnum.FULLFRAME); } }
                
        //ctor
        public StructualFrame(CellBase parent)
        {
            CurrentPlacement = 0;
            this.Parent = parent;
        }

        //Methods
        private Color GetRenderColor(StructureFrameEnum frame)
        {
            if(IsOccuppied(frame))
                return Color.Red;

            //If the current blueprint is not allowed to occupy this tile
            if (StructuralControlUnit.CurrentBlueprintAllowance != null)
            {
                StructureFrameEnum validFrames = ConversionTools.FrameAllowanceToFrames((BlueprintFrameAllowance)StructuralControlUnit.CurrentBlueprintAllowance);

                //If the current blueprint cant occupy this.
                if((validFrames & frame) != frame)
                    return Color.Red;
            }

            return CanOccupy(frame) ? Color.Green : Color.Red;
        }

        private void RenderSemiFrames(SpriteBatch batch, Rectangle drawRect)
        {
            //NORTH
            if ((!Parent.NorthNeighboughOpen) && CanOccupy(StructureFrameEnum.NORTHFRAME) && IsOpenToBlueprint(StructureFrameEnum.NORTHFRAME))
                batch.Draw(ResourceBin.Instance.Get<Texture2D>("FRAMEWALL_NS"),
                    FrameManager.GetFrame(StructureFrameEnum.NORTHFRAME, Parent),
                    GetRenderColor(StructureFrameEnum.NORTHFRAME)); //CanOccupy(StructureFrameEnum.NORTH_WALL) ? Color.Green : Color.Red);

            //SOUTH
            if (!Parent.SouthNeighboughOpen && CanOccupy(StructureFrameEnum.SOUTHFRAME) && IsOpenToBlueprint(StructureFrameEnum.SOUTHFRAME))
                batch.Draw(ResourceBin.Instance.Get<Texture2D>("FRAMEWALL_NS"),
                    FrameManager.GetFrame(StructureFrameEnum.SOUTHFRAME, Parent),
                    GetRenderColor(StructureFrameEnum.SOUTHFRAME)); //CanOccupy(StructureFrameEnum.SOUTH_WALL) ? Color.Green : Color.Red);

            //EAST
            if (!Parent.EastNeighboughOpen && CanOccupy(StructureFrameEnum.EASTFRAME) && IsOpenToBlueprint(StructureFrameEnum.EASTFRAME))
                batch.Draw(ResourceBin.Instance.Get<Texture2D>("FRAMEWALL_EW"),
                    FrameManager.GetFrame(StructureFrameEnum.EASTFRAME, Parent),
                    GetRenderColor(StructureFrameEnum.EASTFRAME)); //CanOccupy(StructureFrameEnum.EAST_WALL) ? Color.Green : Color.Red);

            //West
            if (!Parent.WestNeighboughOpen && CanOccupy(StructureFrameEnum.WESTFRAME) && IsOpenToBlueprint(StructureFrameEnum.WESTFRAME))
                batch.Draw(ResourceBin.Instance.Get<Texture2D>("FRAMEWALL_EW"),
                    FrameManager.GetFrame(StructureFrameEnum.WESTFRAME, Parent),
                    GetRenderColor(StructureFrameEnum.WESTFRAME)); //CanOccupy(StructureFrameEnum.WEST_WALL) ? Color.Green : Color.Red);

            //CENTER
            if (CanOccupy(StructureFrameEnum.CENTERFRAME) && IsOpenToBlueprint(StructureFrameEnum.CENTERFRAME))
                batch.Draw(ResourceBin.Instance.Get<Texture2D>("FRAMEWALL_CN"),
                    FrameManager.GetFrame(StructureFrameEnum.CENTERFRAME, Parent),
                    Center ? Color.Red : Color.Green);
        }
        private void RenderFullFrameFrame(SpriteBatch batch, Rectangle drawRect)
        {
            batch.Draw(ResourceBin.Instance.Get<Texture2D>("FRAMEWALL_CN"),
                    FrameManager.GetFrame(StructureFrameEnum.FULLFRAME, Parent),
                    FullFrame ? Color.Red : Color.Green);
        }

        public void Render(SpriteBatch batch, Rectangle drawRect, GameTime time)
        {
            if (!StructuralControlUnit.IsBlueprintValidForCell(Parent))
                return;

            CellFrameType validTypeForThis = StructuralControlUnit.GetRenderableCellFrame(Parent);

            if(validTypeForThis == 0)
                return;
            else if (validTypeForThis == CellFrameType.SEMIFRAME)
                RenderSemiFrames(batch, drawRect);
            else if (validTypeForThis == CellFrameType.FULLFRAME)
                RenderFullFrameFrame(batch, drawRect);
        }

        public bool PlaceFrame(StructureFrameEnum newPlacement)
        {
            //First, check if the newPlacement is already occupied E.g. we are trying to palce the same Frame
            if (IsOccuppied(newPlacement))
                return false;

            //So now we can do checks 
            bool IsAllowed = false;
            switch (newPlacement)
            {
                case StructureFrameEnum.SOUTHFRAME:
                case StructureFrameEnum.NORTHFRAME:
                    {
                        IsAllowed = !(EastWall | WestWall);
                        break;
                    }

                case StructureFrameEnum.WESTFRAME:
                case StructureFrameEnum.EASTFRAME:
                    {
                        IsAllowed = !(NorthWall | SouthWall);
                        break;
                    }

                case StructureFrameEnum.CENTERFRAME: 
                    { 
                        IsAllowed = !FullFrame;
                        break;
                    }

                case StructureFrameEnum.FULLFRAME:
                    {
                        IsAllowed = !(NorthWall || SouthWall || EastWall || WestWall || Center);
                        break;
                    }

                default:
                    throw new Exception("DEFENDER ERROR: UNRECOGNISED FRAME IN PlaceFrame(" + newPlacement + ")");
            }

            if (IsAllowed)
                CurrentPlacement = CurrentPlacement | newPlacement;

            return IsAllowed;
        }
        public bool IsOccuppied(StructureFrameEnum frame, StructureFrameEnum? with = null)
        {
            if (with == null)
                return (CurrentPlacement & frame) == frame;
            else
                return (with & frame) == frame;
        }
        public bool CanOccupy(StructureFrameEnum frame)
        {
            bool IsAllowed = false;
            switch (frame)
            {
                case StructureFrameEnum.SOUTHFRAME:
                case StructureFrameEnum.NORTHFRAME:
                    {

                        IsAllowed = !(EastWall | WestWall);
                        break;
                    }

                case StructureFrameEnum.WESTFRAME:
                case StructureFrameEnum.EASTFRAME:
                    {
                        IsAllowed = !(NorthWall | SouthWall);
                        break;
                    }

                case StructureFrameEnum.CENTERFRAME:
                    {
                        IsAllowed = true;
                        break;
                    }

                default:
                    throw new Exception("DEFENDER ERROR: UNRECOGNISED FRAME IN CheckFramePlacment(" + frame + ")");
            }
            return IsAllowed && IsOpenToBlueprint(frame);
        }
        public bool IsOpenToBlueprint(StructureFrameEnum frame)
        {
            if (StructuralControlUnit.CurrentBlueprintAllowance == null)
                return false;

            BlueprintFrameAllowance allowance = (BlueprintFrameAllowance)StructuralControlUnit.CurrentBlueprintAllowance;
            StructureFrameEnum allowedAllocations = ConversionTools.FrameAllowanceToFrames(allowance);

            return (allowedAllocations & frame) == frame;
        }
        
        public void FreeFrame(StructureFrameEnum frame)
        {
            CurrentPlacement = CurrentPlacement ^ frame;
        }
    }
}
